﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using MySql.Data.MySqlClient;
using System.Runtime.InteropServices;
using System.Reflection;
using CoreLib.MySQL.Mapping;

namespace CoreLib.MySQL
{
    public class DbSelect : DbBaseQuery<DbSelect>, IDbQuery, IDisposable
    {
        private DbProvider dbProvider;
        private DataTable dataTable = new DataTable();
        private DataRow dataRow;
        private int rowIndex;

        public DbSelect(DbProvider dbProvider)
        {
            this.dbProvider = dbProvider;
            base.TQuery = this;
        }

        public bool Read()
        {
            if (dataTable.Rows.Count <= rowIndex)
                return false;
            this.dataRow = dataTable.Rows[rowIndex++];
            return true;
        }

        public DbSelect From(string table)
        {
            base.table = table;
            return this;
        }

        public DbSelect Select(string column)
        {
            return base.AddParam(column, null);
        }

        public new DbSelect Where(string column, Comparisson comparer, object value)
        {
            return base.Where(column, comparer, value);
        }

        public new DbSelect And(string column, Comparisson comparer, object value)
        {
            return base.And(column, comparer, value);
        }

        public new DbSelect Or(string column, Comparisson comparer, object value)
        {
            return base.Or(column, comparer, value);
        }

        public new DbSelect Limit(int limit)
        {
            return base.Limit(limit);
        }

        public CastableDbObject Read(string column)
        {
            return new CastableDbObject(dataRow[column]);
        }

        public T Read<T>(string column)
        {
            return (T)dataRow[column];
        }

        public void Load<T>(ref T TObject)
        {
            List<DbFieldAttribute> fields = DbMap.GetMap(typeof(T));
            foreach (var field in fields)
            {
                Console.WriteLine(field.ColumnName);

                var property = field.memberInClass as PropertyInfo;
                if (property != null)
                {
                    property.SetValue(TObject, Convert.ChangeType(dataRow[field.ColumnName], property.PropertyType), null);
                }
                else
                {
                    var val = field.memberInClass as FieldInfo;
                    val.SetValue(TObject, Convert.ChangeType(dataRow[field.ColumnName], val.FieldType));
                }
            }
        }

        public unsafe void Execute()
        {
            StringBuilder queryBuilder = new StringBuilder();
            queryBuilder.AppendFormat("Select <C> From {0} <W>", base.table);
            if (base.limit != 0) queryBuilder.AppendFormat(" Limit {0}", base.limit);
            using (MySqlCommand cmd = new MySqlCommand())
            {
#if UNMANAGED
                if (base.queryClauses.Count > 0)
                {
                    string[] columns = new string[base.queryClauses.Count];
                    for (int i = 0; i < columns.Length; i++)
                    {
                        columns[i] = base.GetParameter();
                        QuerySegment* segment = (QuerySegment*)base.queryClauses.Dequeue();
                        cmd.Parameters.AddWithValue(columns[i], new string(segment->Column));
                        Marshal.FreeHGlobal((IntPtr)segment);
                    }
                    queryBuilder.Replace("<C>", string.Join(",",columns));
                }
                else
                {
                    queryBuilder.Replace("<C>", "*");
                }

                string[] locations = new string[base.whereClauses.Count];
                for (int i = 0; i < locations.Length; i++)
                {
                    string param = base.GetParameter();
                    QuerySegment* segment = (QuerySegment*)base.whereClauses.Dequeue();
                    cmd.Parameters.AddWithValue(param, new string(segment->Value));
                    locations[i] = string.Format("{0} {1} {2} {3}", segment->Connector.ToString(), new string(segment->Column), TranslateOperator(segment->Comparisson), param);
                    Marshal.FreeHGlobal((IntPtr)segment);
                }
                queryBuilder.Replace("<W>", string.Join(" ", locations));
#else
                if (base.queryClauses.Count > 0)
                {
                    string[] columns = new string[base.queryClauses.Count];
                    for (int i = 0; i < columns.Length; i++)
                    {
                        columns[i] = base.GetParameter();
                        QuerySegment segment = base.queryClauses.Dequeue();
                        cmd.Parameters.AddWithValue(columns[i], segment.Column);
                    }
                    queryBuilder.Replace("<C>", string.Join(",", columns));
                }
                else
                {
                    queryBuilder.Replace("<C>", "*");
                }

                string[] locations = new string[base.whereClauses.Count];
                for (int i = 0; i < locations.Length; i++)
                {
                    string param = base.GetParameter();
                    QuerySegment segment = (QuerySegment)base.whereClauses.Dequeue();
                    cmd.Parameters.AddWithValue(param, segment.Value);
                    locations[i] = string.Format("{0} {1} {2} {3}", segment.Connector.ToString(), segment.Column, TranslateOperator(segment.Comparisson), param);
                }
                queryBuilder.Replace("<W>", string.Join(" ", locations));
#endif
                cmd.CommandText = queryBuilder.ToString();
                using (cmd.Connection = dbProvider.CreateDbConnection())
                {
                    using (var adapter = new MySql.Data.MySqlClient.MySqlDataAdapter(cmd))
                    {
                        adapter.Fill(this.dataTable);
                        this.rowIndex = 0;
                    }
                }
            }
        }

        public void Dispose()
        {
            this.dataTable.Dispose();
            this.dataRow.Delete();
        }
    }
}
